package cn.aiseminar.aisinterphone;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;

public class BluetoothPhoneService {
	
	private BluetoothAdapter mBTAdapter = null;
	private final String mBTSDPName = "AISInterphoneBTService";
	private final UUID mBTUuid = UUID.fromString("73A06ACC-C5B6-4af5-AAB0-AA9C14F7DAC4"); 
	private ServerAcceptThread mAcceptThread = null;
	private ClientConnectThread mConnectThread = null;
	private DataTransferThread mDataTransferThread = null;
	private Handler mMsgHandler = null;
	
	public static final int MSG_REMOTE_DEVICE_CONNECTED = 0;
	public static final int MSG_CONTENT_READED = 1;
	public static final int MSG_CONTENT_WRITED = 2;
	public static final int MSG_DEVICE_DISCONNECTED = 3;
	public static final int MSG_DEVICE_CONNECT_FAILED = 4;
	public static final int MSG_DEVICE_CONNECT_SUCCESS = 5;
	
	private static BluetoothPhoneService gBTPhoneService = null;
	
	public static BluetoothPhoneService getBluetoothPhoneService()
	{
		if (null == gBTPhoneService)
		{
			gBTPhoneService = new BluetoothPhoneService();
		}
		return gBTPhoneService;
	}
	
	private BluetoothPhoneService()
	{
		mBTAdapter = BluetoothAdapter.getDefaultAdapter();
	}
	
	public Handler getMsgHandler() {
		return mMsgHandler;
	}

	public void setMsgHandler(Handler msgHandler) {
		this.mMsgHandler = msgHandler;
	}
	
	public ArrayList<Device> getPairedDeviceList()
	{
		ArrayList<Device> devList = new ArrayList<Device>();
		
		if (null != mBTAdapter && mBTAdapter.isEnabled())
		{
			Set<BluetoothDevice> pairedDevices = mBTAdapter.getBondedDevices();
			for (BluetoothDevice btDevice : pairedDevices)
			{
				Device dev = new Device();
				dev.setName(btDevice.getName());
				dev.setMacAddress(btDevice.getAddress());
				devList.add(dev);
			}
		}
		return devList;		
	}
	
	public Boolean startDiscovery()
	{
		return mBTAdapter.startDiscovery();
	}
	
	public Boolean cancelDiscovery()
	{
		return mBTAdapter.cancelDiscovery();
	}
	
	public void startAcceptServer()
	{
		if (null != mAcceptThread)
		{
			mAcceptThread.cancel();
		}
		mAcceptThread = new ServerAcceptThread();
		mAcceptThread.start();			
	}
	
	public void connectDevice(String deviceMacAddress)
	{
		if (null != mAcceptThread)
		{
			mAcceptThread.cancel();
		}
		
		if (null != mConnectThread)
		{
			mConnectThread.cancel();
		}
		BluetoothDevice device = mBTAdapter.getRemoteDevice(deviceMacAddress);
		mConnectThread = new ClientConnectThread(device);
		mConnectThread.start();
	}
	
	public void beginTransferData(BluetoothSocket remoteSocket)
	{
		mDataTransferThread = new DataTransferThread(remoteSocket);
		mDataTransferThread.start();
	}
	
	public void stopTransferData()
	{
		if (null != mDataTransferThread)
		{
			mDataTransferThread.cancel();
		}
	}
	
	public void sendNotifyMessage(int messageType, Object obj)
	{
		Message msg = new Message();
		msg.what = messageType;
		msg.obj = obj;					
		mMsgHandler.sendMessage(msg);
	}
	
	public void sendMessageToRemote(String message)
	{
		if (null == mDataTransferThread || null == message || message.isEmpty())
		{
			return;
		}
		mDataTransferThread.write(message.getBytes());
	}
	
	public void sendDataToRemote(byte[] buffer, int offset, int count)
	{
		if (null == mDataTransferThread)
		{
			return;
		}
		mDataTransferThread.write(buffer, offset, count);
	}
	/*
	 * thread to accept incoming connections
	 */
	
	private class ServerAcceptThread extends Thread
	{
		private BluetoothServerSocket mServerSocket = null;
		
		public ServerAcceptThread()
		{
			BluetoothServerSocket tmp = null;
			try
			{
				tmp = mBTAdapter.listenUsingRfcommWithServiceRecord(mBTSDPName, mBTUuid);
			}
			catch (IOException e)
			{	
				
			}
			
			mServerSocket = tmp;
		}

		@Override
		public void run() 
		{
			BluetoothSocket remoteSocket = null;
			while (true) 
			{
				try
				{
					if (null != mServerSocket)
					{
						remoteSocket = mServerSocket.accept();
					}
				}
				catch (IOException e)
				{
					break;
				}
				
				if (null != remoteSocket)
				{					
					sendNotifyMessage(MSG_REMOTE_DEVICE_CONNECTED, remoteSocket.getRemoteDevice());
					beginTransferData(remoteSocket);
										
					try 
					{
						mServerSocket.close();
					} 
					catch (IOException e) 
					{
						
					}
					break;
				}
			}
		}
		
		public void cancel()
		{
			try 
			{
				mServerSocket.close();
			} 
			catch (IOException e) 
			{

			}
		}		
	}
	
	/*
	 * thread to initiate a connection
	 */
	private class ClientConnectThread extends Thread
	{
		private BluetoothSocket mRemoteSocket;
		private BluetoothDevice mRemoteDevice;
		
		public ClientConnectThread(BluetoothDevice device)
		{
			BluetoothSocket tmp = null;
			mRemoteDevice = device;
			try
			{
				tmp = mRemoteDevice.createRfcommSocketToServiceRecord(mBTUuid);
			}
			catch (IOException e)
			{
				
			}
			mRemoteSocket = tmp;
		}

		public void run()
		{
			mBTAdapter.cancelDiscovery();
			try
			{
				mRemoteSocket.connect();
			}
			catch (IOException e)
			{
				try
				{
					mRemoteSocket.close();
					sendNotifyMessage(MSG_DEVICE_CONNECT_FAILED, e.getLocalizedMessage());
				}
				catch (IOException closeE)
				{
					
				}
				return;
			}
			
			sendNotifyMessage(MSG_DEVICE_CONNECT_SUCCESS, null);
			beginTransferData(mRemoteSocket);
		}
		
		public void cancel() 
		{
			try {
				mRemoteSocket.close();
			} catch (IOException e) {
				// e.printStackTrace();
			}
		}
	}
	
	/*
	 * thread to transfer data
	 */
	private class DataTransferThread extends Thread
	{
		private BluetoothSocket mRemoteSocket = null;
		private InputStream mInStream = null;
		private OutputStream mOutStream = null;
		
		public DataTransferThread(BluetoothSocket socket)
		{
			mRemoteSocket = socket;
			InputStream tmpIn = null;
			OutputStream tmpOut = null;
			try
			{
				tmpIn = mRemoteSocket.getInputStream();
				tmpOut = mRemoteSocket.getOutputStream();
			}
			catch (IOException e)
			{
				
			}
			
			mInStream = tmpIn;
			mOutStream = tmpOut;
		}
		
		public void run()
		{
			byte[] buffer = new byte[4096];
			int readBytes = 0;
			
			while (true)
			{
				try
				{
					readBytes = mInStream.read(buffer);
					byte[] readBuffer = new byte[readBytes];
					System.arraycopy(buffer, 0, readBuffer, 0, readBytes);
					mMsgHandler.obtainMessage(MSG_CONTENT_READED, readBytes, -1, readBuffer).sendToTarget();
				}
				catch (IOException e)
				{
					sendNotifyMessage(MSG_DEVICE_DISCONNECTED, null);
					break;
				}
			}
		}
		
		public void write(final byte[] buffer)
		{
			try
			{
				mOutStream.write(buffer);
				sendNotifyMessage(MSG_CONTENT_WRITED, null);
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
		
		public void write(byte[] buffer, int offset, int count)
		{
			try
			{
				mOutStream.write(buffer, offset, count);
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
		
		public void cancel()
		{
			try
			{
				mRemoteSocket.close();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}
}
